package com.gitee.wsl.compose.chart.sample

import androidx.compose.animation.core.TweenSpec
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.unit.dp
import com.gitee.wsl.compose.chart.ChartLayout
import com.gitee.wsl.compose.chart.ExperimentalKoalaPlotApi
import com.gitee.wsl.compose.chart.legend.LegendLocation
import com.gitee.wsl.compose.chart.xy.XYGraph
import com.gitee.wsl.compose.chart.xy.axis.model.CategoryAxisModel
import com.gitee.wsl.compose.chart.xy.axis.model.FloatLinearAxisModel
import com.gitee.wsl.compose.chart.xy.axis.style.rememberAxisStyle
import com.gitee.wsl.compose.chart.xy.line.LinePlot
import com.gitee.wsl.compose.chart.xy.model.PointEntry
import com.gitee.wsl.compose.ui.widget.Symbol
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.mutate
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
import kotlinx.collections.immutable.toPersistentList
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.withContext
import kotlinx.datetime.Clock
import kotlinx.datetime.Instant
import kotlin.math.max
import kotlin.math.min
import kotlin.random.Random

val liveTimeChartSampleView = object : SampleView {
    override val name: String = "Live Time Chart"

    override val thumbnail = @Composable {
        ThumbnailTheme {
            LiveTimeChart(true)
        }
    }

    override val content: @Composable () -> Unit = @Composable {
        LiveTimeChart(false)
    }
}

data class GraphData<X, Y : Comparable<Y>>(
    val xAxis: ImmutableList<X>,
    val yAxis: ImmutableList<Y>,
    val points: ImmutableList<PointEntry<X, Y>>,
    val yRange: ClosedFloatingPointRange<Y>,
)

private const val HistorySize = 20
private const val UpdateDelay = 500L

@OptIn(ExperimentalKoalaPlotApi::class)
@Composable
fun LiveTimeChart(thumbnail: Boolean) {
    val info = remember {
        val x = Clock.System.now().toEpochMilliseconds()
        val y = if (Random.nextBoolean()) {
            1f
        } else {
            -1f
        }
        mutableStateOf(
            GraphData(
                persistentListOf(Instant.fromEpochMilliseconds(x).toString()),
                persistentListOf(y),
                persistentListOf(PointEntry(Instant.fromEpochMilliseconds(x).toString(), y)),
                -1f..1f
            )
        )
    }

    LaunchedEffect(thumbnail) {
        withContext(Dispatchers.Main) {
            var count = 0
            while (isActive) {
                count++
                delay(UpdateDelay)
                val yLast = info.value.yAxis.last()
                val yNext = if (Random.nextBoolean()) {
                    yLast + 1
                } else {
                    yLast - 1
                }

                val x = Clock.System.now().toEpochMilliseconds()
                info.value = info.value.copy(
                    info.value.xAxis.toPersistentList().mutate {
                        it.add(Instant.fromEpochMilliseconds(x).toString())
                    }.takeLast(HistorySize).toImmutableList(),
                    info.value.yAxis.toPersistentList().mutate {
                        it.add(yNext)
                    }.takeLast(HistorySize).toImmutableList(),
                    info.value.points.toPersistentList().mutate {
                        it.add(PointEntry(Instant.fromEpochMilliseconds(x).toString(), yNext))
                    }.takeLast(HistorySize).toImmutableList(),
                    min(info.value.yRange.start, yNext)..max(info.value.yRange.endInclusive, yNext)
                )
            }
        }
    }

    ChartLayout(
        modifier = paddingMod.padding(horizontal = 8.dp),
        title = { ChartTitle("Live Time Chart") },
        legendLocation = LegendLocation.NONE,
    ) {
        XYGraph(
            xAxisModel = CategoryAxisModel(info.value.xAxis),
            yAxisModel = FloatLinearAxisModel(
                range = info.value.yRange,
                minimumMajorTickSpacing = 50.dp,
            ),
            yAxisLabels = {
                if (!thumbnail) {
                    AxisLabel(it.toString())
                }
            },
            xAxisLabels = {
                if (!thumbnail) {
                    AxisLabel(it, Modifier.padding(top = 2.dp))
                }
            },
            yAxisTitle = { },
            xAxisTitle = { },
            xAxisStyle = rememberAxisStyle(labelRotation = 45),
        ) {
            LinePlot(
                info.value.points,
                symbol = { entry: PointEntry<String, Float>, selected: Boolean -> Symbol(fillBrush = SolidColor(Color.Black)) },
                animationSpec = TweenSpec(0)
            )
        }
    }
}
